home *** CD-ROM | disk | FTP | other *** search
/ Gamers Delight 2 / Gamers Delight 2.iso / Aminet / game / role / AMScen_0_9.lha / AMScen / fight.m < prev    next >
Text File  |  1995-01-21  |  43KB  |  1,668 lines

  1. /*
  2.  * Amiga MUD
  3.  *
  4.  * Copyright (c) 1995 by Chris Gray
  5.  */
  6.  
  7. /*
  8.  * fight.m - add player stats, fighting, etc. to the starter dungeon.
  9.  */
  10.  
  11. private tp_fight CreateTable().
  12. use tp_fight
  13.  
  14. /*
  15.  * NOTE: there is a direct dependency on r_arrivals, to send killed
  16.  *    players there.
  17.  */
  18.  
  19. define t_fight STEPS_PER_REGAINED_HIT_POINT 25.
  20. define t_fight BLUTOS_AFTER_DYING 25.
  21. define t_fight RANDOM_MONSTER_LIFE 100.
  22. define t_fight FOREVER_LIFE -1.
  23.  
  24. /* first, the new player properties. Note: these are all the effective values,
  25.    which may include modifications from armour, weapons, spells, etc. */
  26.  
  27. define t_fight p_pInited CreateBoolProp().    /* player has been set up */
  28. define t_fight p_pFightTerse CreateBoolProp().
  29. define t_fight p_pFightSuperTerse CreateBoolProp().
  30. define t_fight p_pHitMax CreateIntProp().    /* max hit points */
  31. define t_fight p_pHitNow CreateIntProp().    /* current hit points */
  32. define t_fight p_pHitCount CreateIntProp().    /* part count for healing */
  33. define t_fight p_pExperience CreateIntProp().    /* experience */
  34. define t_fight p_pLevel CreateIntProp().    /* current level */
  35. define t_fight p_pStrength CreateIntProp().    /* strength */
  36. define t_fight p_pSpeed CreateIntProp().    /* speed/dexterity */
  37. define t_fight p_pProtection CreateIntProp().    /* current armour class */
  38. define t_fight p_pWeapon CreateThingProp().    /* current weapon */
  39. define t_fight p_pShield CreateThingProp().    /* current shield */
  40. define t_fight p_pArmour CreateThingProp().    /* current armour */
  41. define t_fight p_pCurrentTarget CreateThingProp().    /* current opponent */
  42. define t_fight p_pDieNotifyList CreateActionListProp().
  43. define t_fight p_pNewMonster CreateThingProp(). /* temp during creation */
  44. define t_fight p_pWieldChecker CreateActionProp().     /* allow player check */
  45.  
  46. /* monster-only properties */
  47.  
  48. define t_fight p_mActions CreateStringProp().    /* single string of actions */
  49. define t_fight p_mActionIndexes CreateIntListProp().    /* indices of them */
  50. define t_fight p_mMovesUntilVanish CreateIntProp().    /* when it leaves */
  51. define t_fight p_mInitAction CreateActionProp().    /* startup action */
  52. define t_fight p_mMoveAction CreateActionProp().    /* step action */
  53. define t_fight p_mAfterMoveAction CreateActionProp().    /* on enter new room */
  54. define t_fight p_mFightAction CreateActionProp().    /* fight action */
  55. define t_fight p_mSpecialAction CreateActionProp().    /* action to do */
  56. define t_fight p_mKillAction CreateActionProp().    /* action on kill */
  57. define t_fight p_mCreateAction CreateActionProp().    /* something new */
  58. define t_fight p_mArriveAction CreateActionProp().    /* something comes */
  59. define t_fight p_mArrivedAction CreateActionProp().    /* check new loc */
  60. define t_fight p_mExpired CreateBoolProp().        /* it has "died" */
  61. define t_fight p_mBlocker CreateBoolProp().        /* it can block */
  62. define t_fight p_mHunting CreateBoolProp().        /* look for target */
  63.  
  64. /* properties for weapons/shield/armour */
  65.  
  66. define t_fight p_oHitBonus CreateIntProp().    /* hitpoints bonus */
  67. define t_fight p_oStrBonus CreateIntProp().    /* strength bonus */
  68. define t_fight p_oSpeedBonus CreateIntProp().    /* speed bonus */
  69. define t_fight p_oArmourProt CreateIntProp().    /* armour protection bonus */
  70. define t_fight p_oShieldProt CreateIntProp().    /* shield protection bonus */
  71. define t_fight p_oAccuracy CreateIntProp().    /* base accuracy level */
  72. define t_fight p_oDamage CreateIntProp().    /* base damage level */
  73. define t_fight p_oHealing CreateIntProp().    /* strength of heal */
  74. define t_fight p_oWieldChecker CreateActionProp().    /* when wield a weapon */
  75.  
  76. /* properties on rooms */
  77.  
  78. define t_fight p_rNoGenerateMonsters CreateBoolProp()./* no new baddies */
  79. define t_fight p_rMonsterList CreateThingListProp().  /* what might turn up */
  80. define t_fight p_rMonsterChance CreateIntListProp().    /* diff per person */
  81. define t_fight p_rMonsterTotal CreateIntProp(). /* total of the chances */
  82.  
  83. /* use these for proc forward references */
  84.  
  85. define tp_fight for_MonsterMove CreateActionProp().
  86. define tp_fight forwardReference CreateThing(nil).
  87.  
  88. /*
  89.  * fighterDesc - return additional description for a fighter.
  90.  */
  91.  
  92. define tp_fight proc fighterDesc()string:
  93.     thing it, weapon, shield, armour;
  94.     string name, s, shieldName, armourName;
  95.  
  96.     it := It();
  97.     s := "";
  98.     name := FormatName(it@p_pName);
  99.     weapon := it@p_pWeapon;
  100.     shield := it@p_pShield;
  101.     if shield ~= nil then
  102.     shieldName := FormatName(shield@p_oName);
  103.     fi;
  104.     armour := it@p_pArmour;
  105.     if armour ~= nil then
  106.     armourName := FormatName(armour@p_oName);
  107.     fi;
  108.     if weapon = nil then
  109.     if shield = nil then
  110.         if armour ~= nil then
  111.         s := name + " is wearing " + armourName;
  112.         fi;
  113.     else
  114.         s := name + AAn(" is using", shieldName);
  115.         if armour ~= nil then
  116.         s := s + " and wearing " + armourName;
  117.         fi;
  118.     fi;
  119.     else
  120.     s := name + AAn(" is wielding", FormatName(weapon@p_oName));
  121.     if shield = nil then
  122.         if armour ~= nil then
  123.         s := s + " and wearing " + armourName;
  124.         fi;
  125.     else
  126.         if armour ~= nil then
  127.         s := s + AAn(", using", shieldName);
  128.         s := s + " and wearing " + armourName;
  129.         else
  130.         s := s + AAn(" and using", shieldName);
  131.         fi;
  132.     fi;
  133.     fi;
  134.     if s ~= "" then
  135.     s := " " + s + ".";
  136.     fi;
  137.     s
  138. corp;
  139.  
  140. /*
  141.  * useItem - start using the given item.
  142.  */
  143.  
  144. define tp_fight proc useItem(thing th)void:
  145.     thing me;
  146.     list action la;
  147.  
  148.     me := Me();
  149.     me@p_pHitMax := me@p_pHitMax + th@p_oHitBonus;
  150.     me@p_pStrength := me@p_pStrength + th@p_oStrBonus;
  151.     me@p_pSpeed := me@p_pSpeed + th@p_oSpeedBonus;
  152.     me@p_pProtection := me@p_pProtection + th@p_oArmourProt + th@p_oShieldProt;
  153.     la := me@p_pDescMore;
  154.     if la = nil then
  155.     la := CreateActionList();
  156.     me@p_pDescMore := la;
  157.     fi;
  158.     if FindElement(la, fighterDesc) = -1 then
  159.     AddTail(la, fighterDesc);
  160.     fi;
  161. corp;
  162.  
  163. /*
  164.  * unUseItem - stop using the given item.
  165.  */
  166.  
  167. define tp_fight proc unUseItem(thing th, who)void:
  168.  
  169.     who@p_pHitMax := who@p_pHitMax - th@p_oHitBonus;
  170.     who@p_pStrength := who@p_pStrength - th@p_oStrBonus;
  171.     who@p_pSpeed := who@p_pSpeed - th@p_oSpeedBonus;
  172.     who@p_pProtection := who@p_pProtection -
  173.     th@p_oArmourProt - th@p_oShieldProt;
  174. corp;
  175.  
  176. /* the wear/use/wield routines for armour/shields/weapons */
  177.  
  178. define tp_fight proc armourDrop(thing th)status:
  179.     thing who;
  180.  
  181.     who := th@p_oCarryer;
  182.     if who ~= nil then
  183.     unUseItem(th, who);
  184.     who -- p_pArmour;
  185.     th -- p_oUnGetChecker;
  186.     fi;
  187.     continue
  188. corp;
  189.  
  190. define tp_fight proc shieldDrop(thing th)status:
  191.     thing who;
  192.  
  193.     who := th@p_oCarryer;
  194.     if who ~= nil then
  195.     unUseItem(th, who);
  196.     who -- p_pShield;
  197.     th -- p_oUnGetChecker;
  198.     fi;
  199.     continue
  200. corp;
  201.  
  202. define tp_fight proc weaponDrop(thing th)status:
  203.     thing who;
  204.  
  205.     who := th@p_oCarryer;
  206.     if who ~= nil then
  207.     unUseItem(th, who);
  208.     who -- p_pWeapon;
  209.     th -- p_oUnGetChecker;
  210.     fi;
  211.     continue
  212. corp;
  213.  
  214. define tp_fight proc armourWear()status:
  215.     thing th, me, oldArmour;
  216.  
  217.     th := It();
  218.     me := Me();
  219.     oldArmour := me@p_pArmour;
  220.     if oldArmour ~= nil then
  221.     unUseItem(oldArmour, me);
  222.     oldArmour -- p_oUnGetChecker;
  223.     fi;
  224.     useItem(th);
  225.     th@p_oUnGetChecker := armourDrop;
  226.     me@p_pArmour := th;
  227.     Print("You are now wearing the " + FormatName(th@p_oName) + ".\n");
  228.     succeed
  229. corp;
  230.  
  231. define tp_fight proc shieldUse()status:
  232.     thing th, me, oldShield;
  233.  
  234.     th := It();
  235.     me := Me();
  236.     oldShield := me@p_pShield;
  237.     if oldShield ~= nil then
  238.     unUseItem(oldShield, me);
  239.     oldShield -- p_oUnGetChecker;
  240.     fi;
  241.     useItem(th);
  242.     th@p_oUnGetChecker := shieldDrop;
  243.     me@p_pShield := th;
  244.     Print("You are now using the " + FormatName(th@p_oName) + ".\n");
  245.     succeed
  246. corp;
  247.  
  248. define tp_fight proc weaponWield()status:
  249.     thing th, me, oldWeapon;
  250.  
  251.     th := It();
  252.     me := Me();
  253.     oldWeapon := me@p_pWeapon;
  254.     if oldWeapon ~= nil then
  255.     unUseItem(oldWeapon, me);
  256.     oldWeapon -- p_oUnGetChecker;
  257.     fi;
  258.     useItem(th);
  259.     th@p_oUnGetChecker := weaponDrop;
  260.     me@p_pWeapon := th;
  261.     Print("You are now wielding the " + FormatName(th@p_oName) + ".\n");
  262.     succeed
  263. corp;
  264.  
  265. /*
  266.  * SetupWeapon - set up a model object as a weapon.
  267.  */
  268.  
  269. define t_fight proc public SetupWeapon(thing model; int hitBonus, strBonus,
  270.     speedBonus, armourProt, shieldProt, accuracy, damage)void:
  271.  
  272.     if hitBonus ~= 0 then
  273.     model@p_oHitBonus := hitBonus;
  274.     fi;
  275.     if strBonus ~= 0 then
  276.     model@p_oStrBonus := strBonus;
  277.     fi;
  278.     if speedBonus ~= 0 then
  279.     model@p_oSpeedBonus := speedBonus;
  280.     fi;
  281.     if armourProt ~= 0 then
  282.     model@p_oArmourProt := armourProt;
  283.     model@p_oWearChecker := armourWear;
  284.     fi;
  285.     if shieldProt ~= 0 then
  286.     model@p_oShieldProt := shieldProt;
  287.     model@p_oUseChecker := shieldUse;
  288.     fi;
  289.     if accuracy ~= 0 then
  290.     model@p_oAccuracy := accuracy;
  291.     fi;
  292.     if damage ~= 0 then
  293.     model@p_oDamage := damage;
  294.     model@p_oWieldChecker := weaponWield;
  295.     fi;
  296. corp;
  297.  
  298. /*
  299.  * WeaponSell - companion routine - set up what is for sale.
  300.  */
  301.  
  302. define t_fight proc public WeaponSell(thing room; string name, desc;
  303.     int price, hitBonus, strBonus, speedBonus,
  304.         armourProt, shieldProt, accuracy, damage
  305.     )thing:
  306.     thing model;
  307.  
  308.     model := AddForSale(room, name, desc, price, nil);
  309.     SetupWeapon(model, hitBonus, strBonus, speedBonus, armourProt, shieldProt,
  310.         accuracy, damage);
  311.     model
  312. corp;
  313.  
  314. define tp_fight proc healBuy()status:
  315.     thing th, me;
  316.     int n, hits, delta;
  317.  
  318.     th := It();
  319.     me := Me();
  320.     n := th@p_oHealing;
  321.     n := n / 2 + Random(n / 2);
  322.     hits := me@p_pHitNow;
  323.     delta := me@p_pHitMax - hits;
  324.     if n > delta then
  325.     n := delta;
  326.     fi;
  327.     me@p_pHitNow := hits + n;
  328.     if n = 1 then
  329.     Print("You are healed for one point.\n");
  330.     else
  331.     Print("You are healed for " + IntToString(n) + " points.\n");
  332.     fi;
  333.     if not me@p_pHidden then
  334.     OPrint(FormatName(me@p_pName) + " makes a purchase.\n");
  335.     fi;
  336.     /* We are going to return 'false' to tell StoreBuy to not buy the
  337.        item, but we do want to pay for it. */
  338.     if not me@p_pPrivileged then
  339.     me@p_pMoney := me@p_pMoney - th@p_oPrice;
  340.     fi;
  341.     fail
  342. corp;
  343.  
  344. /*
  345.  * HealSell - set up a healing "object".
  346.  */
  347.  
  348. define t_fight proc public HealSell(thing room; string name;
  349.     int cost, healing)void:
  350.     thing model;
  351.  
  352.     model := AddForSale(room, name, "", cost, healBuy);
  353.     model@p_oHealing := healing;
  354. corp;
  355.  
  356. /*
  357.  * showStats - show the statistics of the given thing (monster or player)
  358.  */
  359.  
  360. define tp_fight proc showStats(thing who; bool forceLong)void:
  361.     int ac;
  362.     thing th;
  363.  
  364.     Print(FormatName(who@p_pName));
  365.     Print(": Hit: ");
  366.     IPrint(who@p_pHitNow);
  367.     Print("/");
  368.     IPrint(who@p_pHitMax);
  369.     Print(" Exp: ");
  370.     IPrint(who@p_pExperience);
  371.     Print(" Lvl: ");
  372.     IPrint(who@p_pLevel);
  373.     Print(" Str: ");
  374.     IPrint(who@p_pStrength);
  375.     Print(" Spd: ");
  376.     IPrint(who@p_pSpeed);
  377.     Print(" AC: ");
  378.     ac := who@p_pProtection;
  379.     if ac > 0 then
  380.     Print("+");
  381.     fi;
  382.     IPrint(ac);
  383.     Print(" Bl: ");
  384.     IPrint(who@p_pMoney);
  385.     Print("\n");
  386.     if forceLong or not who@p_pFightTerse then
  387.     th := who@p_pWeapon;
  388.     if th ~= nil then
  389.         Print("Weapon: ");
  390.         Print(FormatName(th@p_oName));
  391.         Print("\n");
  392.     fi;
  393.     th := who@p_pShield;
  394.     if th ~= nil then
  395.         Print("Shield: ");
  396.         Print(FormatName(th@p_oName));
  397.         Print("\n");
  398.     fi;
  399.     th := who@p_pArmour;
  400.     if th ~= nil then
  401.         Print("Armour: ");
  402.         Print(FormatName(th@p_oName));
  403.         Print("\n");
  404.     fi;
  405.     fi;
  406. corp;
  407.  
  408. /**************************************************************************\
  409. *                                       *
  410. *    Code, etc. for monsters                        *
  411. *                                       *
  412. \**************************************************************************/
  413.  
  414. /* NOTE: each monster points at the player it is currently attacking, which
  415.    is usually the player which caused it to come into being, but can be
  416.    another, if that other attacked the monster. Players point at the monster
  417.    they most recently created or attacked, but those pointers are subject
  418.    to testing before they are used, since the monsters can leave. */
  419.  
  420. /* the fighting code */
  421.  
  422. /*
  423.  * AddExperience - do all needed on an experience gain or loss.
  424.  */
  425.  
  426. define t_fight proc public AddExperience(thing thePlayer; int n)void:
  427.     int level, value;
  428.  
  429.     n := n + thePlayer@p_pExperience;
  430.     thePlayer@p_pExperience := n;
  431.     level := 0;
  432.     value := 10;
  433.     while value <= n do
  434.     value := value * 10;
  435.     level := level + 1;
  436.     od;
  437.     value := thePlayer@p_pLevel;
  438.     if level ~= value then
  439.     thePlayer@p_pLevel := level;
  440.     if level > value then
  441.         SPrint(thePlayer, "You have gained a level!\n");
  442.         if Random(2) = 0 then
  443.         SPrint(thePlayer, "You gain speed.\n");
  444.         thePlayer@p_pSpeed := thePlayer@p_pSpeed + 1;
  445.         else
  446.         SPrint(thePlayer, "You gain strength.\n");
  447.         thePlayer@p_pStrength := thePlayer@p_pStrength + 1;
  448.         fi;
  449.         thePlayer@p_pHitMax := thePlayer@p_pHitMax + 8 + Random(5);
  450.     else
  451.         SPrint(thePlayer, "You have lost a level!\n");
  452.         if thePlayer@p_pSpeed > thePlayer@p_pStrength or
  453.         thePlayer@p_pSpeed = thePlayer@p_pStrength and Random(2) = 0
  454.         then
  455.         SPrint(thePlayer, "You lose speed.\n");
  456.         thePlayer@p_pSpeed := thePlayer@p_pSpeed - 1;
  457.         else
  458.         SPrint(thePlayer, "You lose strength.\n");
  459.         thePlayer@p_pStrength := thePlayer@p_pStrength - 1;
  460.         fi;
  461.         n := thePlayer@p_pHitMax - 8 - Random(5);
  462.         if n < 10 then
  463.         n := 10;
  464.         fi;
  465.         thePlayer@p_pHitMax := n;
  466.     fi;
  467.     fi;
  468. corp;
  469.  
  470. /*
  471.  * FindLoot - the player finds loot from a kill.
  472.  */
  473.  
  474. define t_fight proc public FindLoot(int amount)void:
  475.     thing me;
  476.  
  477.     if amount = 0 then
  478.     amount := 1;
  479.     fi;
  480.     if amount = 1 then
  481.     Print("You found one bluto of loot.\n");
  482.     else
  483.     Print("You found ");
  484.     IPrint(amount);
  485.     Print(" blutos of loot.\n");
  486.     fi;
  487.     me := Me();
  488.     me@p_pMoney := me@p_pMoney + amount;
  489. corp;
  490.  
  491. /*
  492.  * part of KillPlayer.
  493.  */
  494.  
  495. define tp_fight proc showAndLook()status:
  496.  
  497.     ShowIcon();
  498.     ignore ShowRoomToMe(true);
  499.     continue
  500. corp;
  501.  
  502. /*
  503.  * KillPlayer - general routine for killing a player. Send him back to the
  504.  *    arrivals room with no belongings and 25 blutos.
  505.  *    NOTE: if a player tells Packrat to go into the proving grounds, she
  506.  *    will be set up to fight, just as if she were a player. Then, if
  507.  *    someone kills her, this routine will be called by her!
  508.  */
  509.  
  510. define t_fight proc public KillPlayer(thing victim, killer)void:
  511.     list action la;
  512.     list thing lt;
  513.     int count, i;
  514.     action a;
  515.     thing here, item, killerLoc;
  516.     string playerName;
  517.  
  518.     la := victim@p_pDieNotifyList;
  519.     count := Count(la);
  520.     i := 0;
  521.     while i ~= count do
  522.     a := la[i];
  523.     if call(a, bool)(victim) then
  524.         DelElement(la, a);
  525.         count := count - 1;
  526.     else
  527.         i := i + 1;
  528.     fi;
  529.     od;
  530.     here := AgentLocation(victim);
  531.     lt := victim@p_pCarrying;
  532.     count := Count(lt);
  533.     i := 0;
  534.     while i < count do
  535.     item := lt[i];
  536.     if DoDrop(here, victim, item) = continue then
  537.         count := count - 1;
  538.     else
  539.         i := i + 1;
  540.     fi;
  541.     od;
  542.  
  543.     /* this makes for a non-obvious cheat, but I want it disabled anyway */
  544.     playerName := FormatName(victim@p_pName);
  545.     if playerName ~= "Packrat" then
  546.     victim@p_pMoney := BLUTOS_AFTER_DYING;
  547.     fi;
  548.     victim@p_pHitNow := 10;
  549.     victim@p_pExperience := victim@p_pExperience / 2;
  550.     AddExperience(victim, 0);
  551.  
  552.     if victim@p_pHidden then
  553.     victim@p_pHidden := false;
  554.     ABPrint(here, victim, victim,
  555.         "Something fades into view. It is the corpse of " + playerName +
  556.         "!\n");
  557.     fi;
  558.     ABPrint(here, victim, victim,
  559.     "The corpse just fades away, but you catch a glimpse from the corner "
  560.     "of your eye of a pale cloud floating off.\n");
  561.     ignore ForceAction(victim, DoUnShowIcon);
  562.     SetAgentLocation(victim, r_arrivals);
  563.     SPrint(victim,
  564. "You feel an abrupt tearing of the local continuum as your essence "
  565. "departs from your ravaged body. Looking down, you see your corpse fade "
  566. "away to nothing, leaving little sign of your recent presence. A feeling "
  567. "of lightness pervades your being, and you begin to float away from the "
  568. "scene. The floating quickens and the world becomes a blur of motion. Soon "
  569. "you can again make out your surroundings.\n");
  570.     ABPrint(r_arrivals, victim, victim, "From the corner of your eye "
  571.     "you see a pale cloud float into view, which then solidifies into " +
  572.     FormatName(victim@p_pName) + ".\n");
  573.     ignore ForceAction(victim, showAndLook);
  574.  
  575.     /* did the killer kill off the only light source? */
  576.     if killer ~= victim and not LightAt(here) then
  577.     /* it may have gotten dark there */
  578.     killerLoc := AgentLocation(killer);
  579.     if killerLoc ~= here or not HasLight(killer) then
  580.         ForEachAgent(here, UnShowRoomFromAgent);
  581.         if killerLoc = here then
  582.         if killer = Me() then
  583.             UnShowRoomFromMe();
  584.         else
  585.             UnShowRoomFromAgent(killer);
  586.         fi;
  587.         fi;
  588.     fi;
  589.     fi;
  590. corp;
  591.  
  592. /*
  593.  * MonsterHitPlayer - have the monster hit the player.
  594.  *    Note: this can be executed by the player or by the monster!
  595.  *    Return 'true' if the player is still alive.
  596.  */
  597.  
  598. define t_fight proc public MonsterHitPlayer(thing monster,thePlayer,where)bool:
  599.     string monsterName, playerName;
  600.     int n, hits;
  601.     bool alive;
  602.  
  603.     if thePlayer@p_pCurrentTarget = nil then
  604.     thePlayer@p_pCurrentTarget := monster;
  605.     fi; 
  606.     alive := true;
  607.     n := monster@p_oAccuracy;
  608.     if n ~= 0 and Here()@p_rMonsterList ~= nil then
  609.     /* Special case - if accuracy = 0, the monster never attacks.
  610.        Also, don't let monsters attack in a non-combat area, since
  611.        the player can't hit back! */
  612.     monsterName := FormatName(monster@p_pName);
  613.     playerName := FormatName(thePlayer@p_pName);
  614.     if thePlayer@p_pHidden then
  615.         ABPrint(where, thePlayer, thePlayer,
  616.         monsterName + " attacks something.\n");
  617.     else
  618.         ABPrint(where, thePlayer, thePlayer,
  619.         monsterName + " attacks " + playerName + ".\n");
  620.     fi;
  621.     /* first, does the attack hit? Note: 5 is "standard". */
  622.     n := n * 3 + monster@p_pSpeed - 5;
  623.     if n ~= 0 then
  624.         n := Random(60 / n);
  625.     else
  626.         n := 1;
  627.     fi;
  628.     if n = 0 then
  629.         /* A hit. Get basic damage done by monster */
  630.         n := (monster@p_oDamage + 1) * 4;
  631.         n := (n / 2 + Random(n / 2 + 3)) / 4;
  632.         /* Modify by player armour class */
  633.         n := n + thePlayer@p_pProtection - 9;
  634.         /* and for excessive player strength */
  635.         n := n - (thePlayer@p_pStrength - 3) / 5;
  636.         if n > 0 then
  637.         hits := thePlayer@p_pHitNow;
  638.         hits := hits - n;
  639.         SPrint(thePlayer, monsterName + " hits for " +
  640.             if n = 1 then
  641.             "one point"
  642.             else
  643.             IntToString(n) + " points"
  644.             fi +
  645.             ". [" + IntToString(hits) + "/" +
  646.             IntToString(thePlayer@p_pHitMax) + "]\n");
  647.         if hits <= 0 then
  648.             SPrint(thePlayer, "You are killed!\n");
  649.             if not thePlayer@p_pHidden then
  650.             ABPrint(where, thePlayer, thePlayer,
  651.                 monsterName + " kills " + playerName + "!\n");
  652.             fi;
  653.             KillPlayer(thePlayer, monster);
  654.             alive := false;
  655.         else
  656.             thePlayer@p_pHitNow := hits;
  657.         fi;
  658.         else
  659.         if not thePlayer@p_pFightSuperTerse then
  660.             SPrint(thePlayer,
  661.             monsterName + " hits but does no damage.\n");
  662.         fi;
  663.         fi;
  664.     else
  665.         if not thePlayer@p_pFightTerse then
  666.         SPrint(thePlayer, monsterName + " attacks but misses.\n");
  667.         fi;
  668.     fi;
  669.     fi;
  670.     alive
  671. corp;
  672.  
  673. /*
  674.  * KillMonster - normal stuff for killing a monster.
  675.  */
  676.  
  677. define t_fight proc KillMonster(thing monster)void:
  678.     string monsterName;
  679.     thing me, here;
  680.     list thing lt;
  681.     int i;
  682.  
  683.     me := Me();
  684.     monsterName := FormatName(monster@p_pName);
  685.     Print(monsterName + " is killed!\n");
  686.     if me@p_pHidden then
  687.     OPrint(monsterName + " is killed!\n");
  688.     else
  689.     OPrint(FormatName(me@p_pName) + " kills " + monsterName + "!\n");
  690.     fi;
  691.     me -- p_pCurrentTarget;
  692.     lt := monster@p_pCarrying;
  693.     if lt ~= nil then
  694.     here := Here();
  695.     i := Count(lt);
  696.     while i ~= 0 do
  697.         i := i - 1;
  698.         ignore DoDrop(here, monster, lt[i]);
  699.     od;
  700.     fi;
  701.     if monster ~= me then
  702.     i := monster@p_pMoney;
  703.     if i ~= 0 then
  704.         FindLoot((i + 1) / 2 + Random((i + 1) / 2));
  705.     fi;
  706.     fi;
  707.     ignore ForceAction(monster, DoUnShowIcon);
  708. corp;
  709.  
  710. /*
  711.  * PlayerHitMonster - the player is attacking the monster.
  712.  *    Return 'true' if the monster is still alive.
  713.  *    Note: this is only ever executed by the player.
  714.  */
  715.  
  716. define t_fight proc public PlayerHitMonster(thing thePlayer, monster)bool:
  717.     thing weapon, here;
  718.     string monsterName;
  719.     int n, hits;
  720.     list thing lt;
  721.     bool alive;
  722.     action a;
  723.  
  724.     alive := true;
  725.     monster@p_mMovesUntilVanish := RANDOM_MONSTER_LIFE;
  726.     thePlayer@p_pCurrentTarget := monster;
  727.     if monster@p_pCurrentTarget ~= thePlayer then
  728.     /* if multiple people are bashing on a monster, it randomly
  729.        targets one of them */
  730.     if monster@p_pCurrentTarget = nil or Random(2) = 0 then
  731.         monster@p_pCurrentTarget := thePlayer;
  732.     fi;
  733.     fi;
  734.     weapon := thePlayer@p_pWeapon;
  735.     monsterName := FormatName(monster@p_pName);
  736.     if thePlayer@p_pHidden then
  737.     OPrint(monsterName + " is attacked.\n");
  738.     else
  739.     OPrint(FormatName(thePlayer@p_pName) + " attacks " + monsterName +
  740.         ".\n");
  741.     fi;
  742.  
  743.     /* first, does the attack hit? Note: 5 is "standard". */
  744.     if weapon = nil then
  745.     n := 5;
  746.     else
  747.     n := weapon@p_oAccuracy;
  748.     fi;
  749.     n := (n + thePlayer@p_pLevel) * 3 + thePlayer@p_pSpeed - 5;
  750.     /* weaponless level 0 character has one chance in 4 of hitting */
  751.     /* weaponless level 1 - 4 character has one chance in 3 of hitting */
  752.     /* weaponless level 5 - 10 character has one chance in 2 of hitting */
  753.     /* weaponless level 11 and up character always hits */
  754.     if n ~= 0 then
  755.     n := Random(60 / n);
  756.     else
  757.     n := 1;
  758.     fi;
  759.     if n = 0 then
  760.     /* A hit! How much damage? */
  761.     if weapon = nil then
  762.         n := 3;
  763.     else
  764.         n := weapon@p_oDamage;
  765.     fi;
  766.     n := (n + thePlayer@p_pStrength) * 2;
  767.     n := (n / 2 + Random(n / 2 + 3)) / 4;
  768.     /* Modify by monster armour class */
  769.     n := n + monster@p_pProtection - 9;
  770.     if n > 0 then
  771.         Print("You hit for " + IntToString(n) +
  772.         if n = 1 then " point.\n" else " points.\n" fi);
  773.         hits := monster@p_pHitNow;
  774.         if n >= hits then
  775.         /* it is killed */
  776.         a := monster@p_mKillAction;
  777.         if a ~= nil then
  778.             alive := call(a, bool)(monster);
  779.         else
  780.             alive := false;
  781.             KillMonster(monster);
  782.             DestroyMachine(monster);
  783.             AddExperience(thePlayer, hits);
  784.         fi;
  785.         else
  786.         monster@p_pHitNow := hits - n;
  787.         AddExperience(thePlayer, n);
  788.         fi;
  789.     else
  790.         if not thePlayer@p_pFightSuperTerse then
  791.         Print("You hit " + monsterName + " but do no damage.\n");
  792.         fi;
  793.     fi;
  794.     else
  795.     if not thePlayer@p_pFightTerse then
  796.         Print("You attack " + monsterName + " but miss.\n");
  797.     fi;
  798.     fi;
  799.     alive
  800. corp;
  801.  
  802. /*
  803.  * PlayerHitPlayer - the current player is attacking the given player.
  804.  */
  805.  
  806. define t_fight proc public PlayerHitPlayer(thing target)void:
  807.     thing here, thePlayer, weapon;
  808.     string playerName, targetName, points;
  809.     int n, hits;
  810.  
  811.     here := Here();
  812.     thePlayer := Me();
  813.     playerName := FormatName(thePlayer@p_pName);
  814.     targetName := FormatName(target@p_pName);
  815.     if target@p_pInited then
  816.     if not thePlayer@p_pHidden and not target@p_pHidden then
  817.         ABPrint(here, target, thePlayer,
  818.         playerName + " attacks " + targetName + ".\n");
  819.     fi;
  820.     weapon := thePlayer@p_pWeapon;
  821.  
  822.     /* first, does the attack hit? Note: 5 is "standard". */
  823.     if weapon = nil then
  824.         n := 5;
  825.     else
  826.         n := weapon@p_oAccuracy;
  827.     fi;
  828.     n := n + thePlayer@p_pLevel;
  829.     if n ~= 0 then
  830.         n := (Random(200 / n) + 5) / 10;
  831.     else
  832.         n := 1;
  833.     fi;
  834.     if n = 0 then
  835.         /* A hit! How much damage? */
  836.         if weapon = nil then
  837.         n := 5;
  838.         else
  839.         n := weapon@p_oDamage;
  840.         fi;
  841.         n := n + thePlayer@p_pStrength / 5;
  842.         n := n / 2 + Random(n / 2) + 1;
  843.         /* Modify by target armour class */
  844.         n := n + target@p_pProtection - 9;
  845.         /* and for excessive player strength */
  846.         n := n - target@p_pStrength / 5;
  847.         if n > 0 then
  848.         hits := target@p_pHitNow;
  849.         SPrint(target, playerName +
  850.             " attacks you and hits for " +
  851.             if n = 1 then
  852.             "one point"
  853.             else
  854.             IntToString(n) + " points"
  855.             fi + ". [" + IntToString(hits - n) + "/" +
  856.             IntToString(hits) + "]\n");
  857.         Print("You hit for " + if n = 1 then "one point" else
  858.             IntToString(n) + " points" fi + ".\n");
  859.         if n >= hits then
  860.             /* target is killed */
  861.             n := hits;
  862.             if not thePlayer@p_pHidden and not target@p_pHidden then
  863.             ABPrint(here, target, thePlayer,
  864.                 playerName + " kills " + targetName + "!\n");
  865.             fi;
  866.             SPrint(target, playerName + " has killed you!\n");
  867.             Print(targetName + " is killed!\n");
  868.             if target@p_pMoney ~= 0 then
  869.             FindLoot(target@p_pMoney);
  870.             fi;
  871.             KillPlayer(target, thePlayer);
  872.         else
  873.             target@p_pHitNow := hits - n;
  874.         fi;
  875.         AddExperience(thePlayer, n);
  876.         else
  877.         if not target@p_pFightSuperTerse then
  878.             SPrint(target, playerName +
  879.             " attacks you and hits, but does no damage.\n");
  880.         fi;
  881.         if not thePlayer@p_pFightSuperTerse then
  882.             Print("You hit " + targetName + " but do no damage.\n");
  883.         fi;
  884.         fi;
  885.     else
  886.         if not target@p_pFightTerse then
  887.         SPrint(target, playerName + " attacks you but misses.\n");
  888.         fi;
  889.         if not thePlayer@p_pFightTerse then
  890.         Print("You attack " + targetName + " but miss.\n");
  891.         fi;
  892.     fi;
  893.     else
  894.     Print(targetName + " is not set up for fighting.\n");
  895.     fi;
  896. corp;
  897.  
  898. /*
  899.  * InitMonsterModels - clear a room for a new opponent set.
  900.  */
  901.  
  902. define t_fight proc public InitMonsterModels(thing room; int initTotal)void:
  903.  
  904.     room@p_rMonsterList := CreateThingList();
  905.     room@p_rMonsterChance := CreateIntList();
  906.     room@p_rMonsterTotal := initTotal;
  907. corp;
  908.  
  909. /*
  910.  * AddPossibleMonster - add a monster to a room's opponent list.
  911.  */
  912.  
  913. define t_fight proc public AddPossibleMonster(thing room, monster;
  914.     int likelihood)void:
  915.  
  916.     AddTail(room@p_rMonsterList, monster);
  917.     AddTail(room@p_rMonsterChance, likelihood);
  918.     room@p_rMonsterTotal := room@p_rMonsterTotal + likelihood;
  919. corp;
  920.  
  921. /*
  922.  * doRunAway - part of RunAwaySoon, and hence of RunAway.
  923.  */
  924.  
  925. define tp_fight proc doRunAway()void:
  926.     int dir;
  927.  
  928.     dir := Random(12);
  929.     if not call(forwardReference@for_MonsterMove, bool)(Me(), dir) then
  930.     After(1, doRunAway);
  931.     fi;
  932. corp;
  933.  
  934. /*
  935.  * RunAwaySoon - the current monster panics and tries hard to leave this
  936.  *    location. This routine returns a 'status', so that it can be called
  937.  *    via 'ForceAction'.
  938.  */
  939.  
  940. define t_fight proc public RunAwaySoon()status:
  941.  
  942.     OPrint(FormatName(Me()@p_pName) + " panics!\n");
  943.     After(0, doRunAway);
  944.     continue
  945. corp;
  946.  
  947. /*
  948.  * RunAway - something has happened to cause a monster to want to run away.
  949.  *    Arrange for it to try to do so. This routine is not normally called
  950.  *    by the monster that wants to run away, so we use ForceAction and
  951.  *    After to make it work out right.
  952.  */
  953.  
  954. define t_fight proc public RunAway(thing who)void:
  955.  
  956.     ignore ForceAction(who, RunAwaySoon);
  957. corp;
  958.  
  959. /*
  960.  * MonsterAction - a monster does one of its random "actions".
  961.  */
  962.  
  963. define t_fight proc public MonsterAction(thing me)void:
  964.     list int li;
  965.     string name, s;
  966.     int pos, n;
  967.  
  968.     if LightAt(Here()) then
  969.     li := me@p_mActionIndexes;
  970.     name := FormatName(me@p_pName);
  971.     if li = nil then
  972.         OPrint(name + " runs around.\n");
  973.     else
  974.         s := me@p_mActions;
  975.         n := Random(Count(li));
  976.         if n = 0 then
  977.         pos := 0;
  978.         else
  979.         pos := li[n - 1];
  980.         fi;
  981.         n := li[n];
  982.         s := SubString(s, pos, n - pos);
  983.         OPrint(name + " " + s + ".\n");
  984.     fi;
  985.     else
  986.     OPrint("You hear a noise.\n");
  987.     fi;
  988. corp;
  989.  
  990. /*
  991.  * checkMonsterArrival - a monster has moved somewhere - inform each other
  992.  *    monster that cares.
  993.  */
  994.  
  995. define tp_fight proc checkMonsterArrival(thing target)void:
  996.     action a;
  997.  
  998.     a := target@p_mArriveAction;
  999.     if a ~= nil then
  1000.     call(a, void)(target, Me());
  1001.     fi;
  1002. corp;
  1003.  
  1004. /*
  1005.  * PickNewTarget - the monster has no target here. Randomly pick one.
  1006.  */
  1007.  
  1008. define tp_fight proc huntTarget(thing who)void:
  1009.     thing me;
  1010.  
  1011.     me := Me();
  1012.     if me@p_mHunting and who@p_pStandard then
  1013.     me -- p_mHunting;
  1014.     me@p_pCurrentTarget := who;
  1015.     ignore MonsterHitPlayer(me, who, Here());
  1016.     fi;
  1017. corp;
  1018.  
  1019. define t_fight proc public PickNewTarget()void:
  1020.  
  1021.     Me()@p_mHunting := true;
  1022.     ForEachAgent(Here(), huntTarget);
  1023. corp;
  1024.  
  1025. /*
  1026.  * MonsterMove - try to move in given direction and fight attacker.
  1027.  */
  1028.  
  1029. define t_fight proc public MonsterMove(thing me; int dir)bool:
  1030.     thing target, here;
  1031.     action a;
  1032.  
  1033.     if TryToMove(dir) then
  1034.     MachineMove(dir);
  1035.     a := me@p_mArrivedAction;
  1036.     if a ~= nil then
  1037.         call(a, void)();
  1038.     fi;
  1039.     here := Here();
  1040.     ForEachAgent(here, checkMonsterArrival);
  1041.     target := me@p_pCurrentTarget;
  1042.     /* if our current target is here - attack! */
  1043.     if target ~= nil and AgentLocation(target) = here then
  1044.         if LightAt(here) then
  1045.         ignore MonsterHitPlayer(me, target, here);
  1046.         else
  1047.         OPrint("You hear a noise.\n");
  1048.         fi;
  1049.     elif Random(5) = 0 then
  1050.         PickNewTarget();
  1051.     fi;
  1052.     true
  1053.     else
  1054.     false
  1055.     fi
  1056. corp;
  1057.  
  1058. forwardReference@for_MonsterMove := MonsterMove.
  1059.  
  1060. /*
  1061.  * DoMonsterMove - attack target, move in random direction or do "action"
  1062.  */
  1063.  
  1064. define t_fight proc public DoMonsterMove(thing me)void:
  1065.     thing target, here;
  1066.     action a;
  1067.  
  1068.     here := Here();
  1069.     if Random(3) = 0 then
  1070.     target := me@p_pCurrentTarget;
  1071.     if target ~= nil and AgentLocation(target) = here then
  1072.         if LightAt(here) then
  1073.         ignore MonsterHitPlayer(me, target, here);
  1074.         else
  1075.         OPrint("You hear a noise.\n");
  1076.         fi;
  1077.     elif Random(5) = 0 then
  1078.         PickNewTarget();
  1079.     fi;
  1080.     else
  1081.     if MonsterMove(me, Random(12)) then
  1082.         a := me@p_mAfterMoveAction;
  1083.         if a ~= nil then
  1084.         call(a, void)();
  1085.         fi;
  1086.     else
  1087.         MonsterAction(me);
  1088.     fi;
  1089.     fi;
  1090. corp;
  1091.  
  1092. /*
  1093.  * MonsterStillMoving - see if monster leaves, and if so, nuke it.
  1094.  */
  1095.  
  1096. define t_fight proc public MonsterStillMoving(thing me; action dieAction)bool:
  1097.     thing obj, target;
  1098.     int n, count;
  1099.     list thing lt;
  1100.  
  1101.     n := me@p_mMovesUntilVanish;
  1102.     if n = FOREVER_LIFE then
  1103.     true
  1104.     elif n <= 0 then
  1105.     me@p_mExpired := true;
  1106.     if dieAction ~= nil then
  1107.         call(dieAction, void)(me);
  1108.     fi;
  1109.     if LightAt(Here()) then
  1110.         OPrint(FormatName(me@p_pName) + " leaves.\n");
  1111.         ForEachAgent(Here(), UnShowIconOnce);
  1112.     fi;
  1113.     target := me@p_pCurrentTarget;
  1114.     if target ~= nil then
  1115.         if target@p_pCurrentTarget = me then
  1116.         target -- p_pCurrentTarget;
  1117.         fi;
  1118.     fi;
  1119.     lt := me@p_pCarrying;
  1120.     if lt ~= nil then
  1121.         count := Count(lt);
  1122.         while count ~= 0 do
  1123.         count := count - 1;
  1124.         obj := lt[count];
  1125.         ZapObject(obj);
  1126.         DelElement(lt, obj);
  1127.         od;
  1128.     fi;
  1129.     /* After this point, the monster has no properties */
  1130.     DestroyMachine(me);
  1131.     /* At this point, the monster does not exist, so this routine must
  1132.        do nothing but exit. In particular, OPrint is a no-no. */
  1133.     /* NOTE: because of a possible reference from a player who attacked
  1134.        the monster and therefore has a reference to its thing as that
  1135.        player's p_pCurrentTarget, the thing, although emptied out,
  1136.        will not go away until all such players delete their reference. */
  1137.     false
  1138.     else
  1139.     me@p_mMovesUntilVanish := n - 1;
  1140.     true
  1141.     fi
  1142. corp;
  1143.  
  1144. /*
  1145.  * MonsterReschedule - setup to do his step again.
  1146.  */
  1147.  
  1148. define t_fight proc public MonsterReschedule(thing monster)void:
  1149.     int n;
  1150.  
  1151.     n := monster@p_pSpeed;
  1152.     if n = 0 then
  1153.     n := 100;
  1154.     else
  1155.     n := 100 / n;
  1156.     fi;
  1157.     After(Random(n) + 1, monster@p_mMoveAction);
  1158. corp;
  1159.  
  1160. /*
  1161.  * DummyMonsterInit - init routine that just does the Reschedule.
  1162.  */
  1163.  
  1164. define t_fight proc public DummyMonsterInit()void:
  1165.  
  1166.     MonsterReschedule(Me());
  1167. corp;
  1168.  
  1169. /*
  1170.  * MonsterInit - initialization of a random moving monster.
  1171.  */
  1172.  
  1173. define t_fight proc public MonsterInit()void:
  1174.     thing me;
  1175.  
  1176.     me := Me();
  1177.     ignore SetMachineActive(me, me@p_mMoveAction);
  1178.     if LightAt(Here()) then
  1179.     OPrint(FormatName(me@p_pName) + " has appeared.\n");
  1180.     ForEachAgent(Here(), ShowIconOnce);
  1181.     fi;
  1182.     MonsterReschedule(me);
  1183. corp;
  1184.  
  1185. /*
  1186.  * MonsterNoNo - standard monster response to many actions.
  1187.  */
  1188.  
  1189. define t_fight proc public MonsterNoNo()status:
  1190.  
  1191.     Print("You don't want to do that!\n");
  1192.     fail
  1193. corp;
  1194.  
  1195. define t_fight GenericMonster CreateThing(nil).
  1196. GenericMonster@p_pName := "MONSTER;GENERIC".
  1197. GenericMonster@p_oTouchChecker := MonsterNoNo.
  1198. GenericMonster@p_oSmellChecker := MonsterNoNo.
  1199. GenericMonster@p_oPushChecker := MonsterNoNo.
  1200. GenericMonster@p_oPullChecker := MonsterNoNo.
  1201. GenericMonster@p_oTurnChecker := MonsterNoNo.
  1202. GenericMonster@p_oLiftChecker := MonsterNoNo.
  1203. GenericMonster@p_oLowerChecker := MonsterNoNo.
  1204. GenericMonster@p_oEatChecker := MonsterNoNo.
  1205. SetThingStatus(GenericMonster, ts_readonly).
  1206.  
  1207. /*
  1208.  * CreateMonsterModel - create a new model monster.
  1209.  */
  1210.  
  1211. define t_fight proc public CreateMonsterModel(string name, desc;
  1212.     action initAction, moveAction;
  1213.     int hits, speed, protection, accuracy, damage, money)thing:
  1214.     thing model;
  1215.  
  1216.     model := CreateThing(GenericMonster);
  1217.     SetThingStatus(model, ts_readonly);
  1218.     model@p_pName := name;
  1219.     if desc ~= "" then
  1220.     model@p_pDesc := desc;
  1221.     else
  1222.     model@p_pDesc := "This is an ordinary " + FormatName(name) + ".";
  1223.     fi;
  1224.     if initAction ~= nil then
  1225.     model@p_mInitAction := initAction;
  1226.     fi;
  1227.     model@p_mMoveAction := moveAction;
  1228.     model@p_pHitMax := hits;
  1229.     model@p_pSpeed := speed;
  1230.     model@p_pProtection := protection;
  1231.     model@p_oAccuracy := accuracy;
  1232.     model@p_oDamage := damage;
  1233.     if money ~= 0 then
  1234.     model@p_pMoney := money;
  1235.     fi;
  1236.     model
  1237. corp;
  1238.  
  1239. /*
  1240.  * AddModelAction - add a 'moves around' action to a model monster.
  1241.  *    Note: keep in mind that these messages are seen by both the player
  1242.  *    involved and by any bystanders. (So don't use 'you'!).
  1243.  */
  1244.  
  1245. define t_fight proc public AddModelAction(thing model; string a)void:
  1246.     list int li;
  1247.     int len;
  1248.  
  1249.     li := model@p_mActionIndexes;
  1250.     if li = nil then
  1251.     li := CreateIntList();
  1252.     model@p_mActionIndexes := li;
  1253.     model@p_mActions := a;
  1254.     else
  1255.     a := model@p_mActions + a;
  1256.     model@p_mActions := a;
  1257.     fi;
  1258.     AddTail(li, Length(a));
  1259. corp;
  1260.  
  1261. /*
  1262.  * checkMonsterCreation - a new monster has arrived. Tell all who care.
  1263.  */
  1264.  
  1265. define tp_fight proc checkMonsterCreation(thing target)void:
  1266.     action a;
  1267.  
  1268.     a := target@p_mCreateAction;
  1269.     if a ~= nil then
  1270.     call(a, void)(target, Me()@p_pNewMonster);
  1271.     fi;
  1272. corp;
  1273.  
  1274. /*
  1275.  * CreateMonster - create a new monster with random hitpoints.
  1276.  *    Note: this routine assumes it is called by a player.
  1277.  */
  1278.  
  1279. define t_fight proc public CreateMonster(thing creator, model, where)thing:
  1280.     thing monster;
  1281.     int hits;
  1282.  
  1283.     monster := CreateThing(model);
  1284.     SetupMachine(monster);
  1285.     hits := model@p_pHitMax;
  1286.     hits := hits / 2 + Random(hits / 2) + 1;
  1287.     monster@p_pHitMax := hits;
  1288.     monster@p_pHitNow := hits;
  1289.     monster@p_mMovesUntilVanish := RANDOM_MONSTER_LIFE;
  1290.     monster@p_pCurrentTarget := creator;
  1291.     monster@p_mExpired := false;
  1292.     CreateMachine(model@p_pName, monster, where, model@p_mInitAction);
  1293.     creator@p_pCurrentTarget := monster;
  1294.     Me()@p_pNewMonster := monster;
  1295.     ForEachAgent(where, checkMonsterCreation);
  1296.     Me() -- p_pNewMonster;
  1297.     monster
  1298. corp;
  1299.  
  1300. /*
  1301.  * CreateSpecificMonster - create a copy of a model monster.
  1302.  */
  1303.  
  1304. define t_fight proc public CreateSpecificMonster(
  1305.     thing creator, model, where)thing:
  1306.     thing monster;
  1307.  
  1308.     monster := CreateThing(model);
  1309.     SetupMachine(monster);
  1310.     monster@p_pHitNow := model@p_pHitMax;
  1311.     monster@p_mMovesUntilVanish := RANDOM_MONSTER_LIFE;
  1312.     monster@p_pCurrentTarget := creator;
  1313.     CreateMachine(model@p_pName, monster, where, model@p_mInitAction);
  1314.     creator@p_pCurrentTarget := monster;
  1315.     Me()@p_pNewMonster := monster;
  1316.     ForEachAgent(where, checkMonsterCreation);
  1317.     Me() -- p_pNewMonster;
  1318.     monster
  1319. corp;
  1320.  
  1321. /*
  1322.  * PickNewMonster - pick a new monster for the player to fight.
  1323.  *    Return the thing of the generated monster, if any.
  1324.  */
  1325.  
  1326. define t_fight proc public PickNewMonster(thing creator, where)thing:
  1327.     list thing lt;
  1328.     list int li;
  1329.     int choice, count;
  1330.  
  1331.     lt := where@p_rMonsterList;
  1332.     if lt ~= nil then
  1333.     li := where@p_rMonsterChance;
  1334.     choice := Random(where@p_rMonsterTotal);
  1335.     count := Count(li);
  1336.     while count ~= 0 and choice >= li[count - 1] do
  1337.         choice := choice - li[count - 1];
  1338.         count := count - 1;
  1339.     od;
  1340.     if count ~= 0 then
  1341.         CreateMonster(creator, lt[count - 1], where)
  1342.     else
  1343.         nil
  1344.     fi
  1345.     else
  1346.     nil
  1347.     fi
  1348. corp;
  1349.  
  1350. /* a 'checker' for healing and generating random monsters */
  1351.  
  1352. define tp_fight proc monsterEnterCheck()status:
  1353.     thing me, monster;
  1354.     int i;
  1355.  
  1356.     me := Me();
  1357.     i := me@p_pHitCount;
  1358.     i := i + 1;
  1359.     if i = STEPS_PER_REGAINED_HIT_POINT then
  1360.     i := me@p_pHitNow;
  1361.     if i < me@p_pHitMax then
  1362.         me@p_pHitNow := i + 1;
  1363.     fi;
  1364.     i := 0;
  1365.     fi;
  1366.     me@p_pHitCount := i;
  1367.     monster := me@p_pCurrentTarget;
  1368.     if monster = nil and not Here()@p_rNoGenerateMonsters then
  1369.     monster := PickNewMonster(me, Here());
  1370.     fi;
  1371.     continue
  1372. corp;
  1373.  
  1374. /* a 'checker' to let monsters try to hit fleeing players */
  1375.  
  1376. define tp_fight proc monsterLeaveCheck(int dir)status:
  1377.     thing me, monster, here;
  1378.     int n;
  1379.     status result;
  1380.  
  1381.     result := continue;
  1382.     me := Me();
  1383.     monster := me@p_pCurrentTarget;
  1384.     if monster ~= nil then
  1385.     here := Here();
  1386.     if AgentLocation(monster) = here then
  1387.         /* the monster I was fighting is still here with me */
  1388.         n := Random(3);
  1389.         if n = 0 and monster@p_mBlocker then
  1390.         if LightAt(here) then
  1391.             Print(FormatName(monster@p_pName) + " blocks you.\n");
  1392.         else
  1393.             Print("Something blocks you.\n");
  1394.         fi;
  1395.         result := fail;
  1396.         else
  1397.         if n = 1 and LightAt(here) then
  1398.             if not MonsterHitPlayer(monster, me, here) then
  1399.             /* player has died - fail the move */
  1400.             result := fail;
  1401.             fi;
  1402.         fi;
  1403.         me -- p_pCurrentTarget;
  1404.         fi;
  1405.     else
  1406.         me -- p_pCurrentTarget;
  1407.     fi;
  1408.     fi;
  1409.     result
  1410. corp;
  1411.  
  1412. /*
  1413.  * StandardAttack - standard code for player attack something.
  1414.  */
  1415.  
  1416. define t_fight proc public StandardAttack(thing thePlayer, target, where)void:
  1417.     int mSpeed;
  1418.  
  1419.     if target@p_pStandard then
  1420.     /* a monster set up like a player, e.g. Packrat after being forced
  1421.        into the proving grounds */
  1422.     if target@p_pInited then
  1423.         PlayerHitPlayer(target);
  1424.     else
  1425.         Print(FormatName(target@p_pName) +
  1426.           " is not set up for fighting.\n");
  1427.     fi;
  1428.     else
  1429.     mSpeed := target@p_pSpeed;
  1430.     if Random(thePlayer@p_pSpeed + mSpeed) < mSpeed then
  1431.         /* monster hits first */
  1432.         if MonsterHitPlayer(target, thePlayer, where) then
  1433.         ignore PlayerHitMonster(thePlayer, target);
  1434.         fi;
  1435.     else
  1436.         /* player hits first */
  1437.         if PlayerHitMonster(thePlayer, target) then
  1438.         ignore MonsterHitPlayer(target, thePlayer, where);
  1439.         fi;
  1440.     fi;
  1441.     fi;
  1442. corp;
  1443.  
  1444. /*
  1445.  * InitFighter - set up a player for fighting. Can be called repeatedly,
  1446.  *    since it checks for duplicates.
  1447.  */
  1448.  
  1449. define t_fight proc public InitFighter(thing fighter)void:
  1450.  
  1451.     if not fighter@p_pInited then
  1452.     fighter@p_pInited := true;
  1453.     fighter@p_pHitMax := 10;
  1454.     fighter@p_pHitNow := 10;
  1455.     fighter@p_pHitCount := 0;
  1456.     fighter@p_pExperience := 0;
  1457.     fighter@p_pStrength := 5;    /* "standard" strength */
  1458.     fighter@p_pSpeed := 5;        /* "standard" speed */
  1459.     fighter@p_pProtection := 9;    /* armour class +9 */
  1460.     fighter@p_pLevel := 0;
  1461.     fighter@p_pDieNotifyList := CreateActionList();
  1462.     AddPlayerEnterChecker(fighter, monsterEnterCheck, false);
  1463.     AddPlayerLeaveChecker(fighter, monsterLeaveCheck, false);
  1464.     SPrint(fighter, "\nCombat initialized!\n\n");
  1465.     fi;
  1466. corp;
  1467.  
  1468. /*
  1469.  * LeaveFighting - utility to call when leaving a fighting area. It returns
  1470.  *    'fail' for monsters, to keep them in, 'succeed' for players. Is
  1471.  *    suitable for a regular move checker.
  1472.  */
  1473.  
  1474. define t_fight proc public LeaveFighting()status:
  1475.     thing fighter;
  1476.  
  1477.     fighter := Me();
  1478.     if fighter@p_pInited then
  1479.     /* a player or someone like Packrat */
  1480.     continue
  1481.     else
  1482.     /* must be a monster - don't let them out */
  1483.     fail
  1484.     fi
  1485. corp;
  1486.  
  1487. /*
  1488.  * HealingBuy - routine to buy healing at a healer.
  1489.  */
  1490.  
  1491. define t_fight proc public HealingBuy(string what)bool:
  1492.     thing me;
  1493.  
  1494.     me := Me();
  1495.     if me@p_pHitNow >= me@p_pHitMax then
  1496.     Print("The proprietor looks at you for a moment, and then frowns "
  1497.         "in puzzlement and says: 'But you are in perfect health!'\n");
  1498.     false
  1499.     else
  1500.     StoreBuy(what)
  1501.     fi
  1502. corp;
  1503.  
  1504. /************************\
  1505. *             *
  1506. * The new commands added *
  1507. *             *
  1508. \************************/
  1509.  
  1510. define tp_fight proc v_status(string who)bool:
  1511.     thing me;
  1512.  
  1513.     me := Me();
  1514.     if who == "full" then
  1515.     if me@p_pInited then
  1516.         showStats(me, true);
  1517.         true
  1518.     else
  1519.         Print("There is no combat status to show you.\n");
  1520.         false
  1521.     fi
  1522.     elif who ~= "" and IsWizard() then
  1523.     me := FindAgent(who);
  1524.     if me ~= nil then
  1525.         showStats(me, true);
  1526.         true
  1527.     else
  1528.         Print("There is no " + who + " here.\n");
  1529.         false
  1530.     fi
  1531.     else
  1532.     if me@p_pInited then
  1533.         showStats(me, false);
  1534.         true
  1535.     else
  1536.         Print("There is no combat status to show you.\n");
  1537.         false
  1538.     fi
  1539.     fi
  1540. corp;
  1541.  
  1542. define tp_fight proc v_wield(string what)bool:
  1543.     VerbCarry("wield", nil, p_oWieldChecker, p_pWieldChecker,
  1544.           "You cannot wield", what)
  1545. corp;
  1546.  
  1547. /*
  1548.  * v_hit - the basic attacking verb.
  1549.  */
  1550.  
  1551. define tp_fight proc v_hit(string what)bool:
  1552.     thing me, target, here;
  1553.     action a;
  1554.  
  1555.     me := Me();
  1556.     here := Here();
  1557.     if what = "" then
  1558.     Print("You must specify who or what you want to attack.\n");
  1559.     false
  1560.     elif not me@p_pInited then
  1561.     Print("You are not yet set up for fighting.\n");
  1562.     false
  1563.     elif not CanSee(here, me) then
  1564.     Print("You can't see to fight.\n");
  1565.     false
  1566.     elif here@p_rMonsterList = nil then
  1567.     Print("This is a non-combat area.\n");
  1568.     false
  1569.     else
  1570.     target := FindAgent(what);
  1571.     if target = nil then
  1572.         if FindName(here@p_rContents, p_oName, what) ~= fail or
  1573.         FindName(me@p_pCarrying, p_oName, what) ~= fail or
  1574.         MatchName(here@p_rScenery, what) ~= -1
  1575.         then
  1576.         Print("You can only attack players or monsters.\n");
  1577.         else
  1578.         Print(IsAre("There", "no", FormatName(what), "here.\n"));
  1579.         fi;
  1580.         false
  1581.     else
  1582.         if ThingCharacter(target) ~= nil then
  1583.         if target = me then
  1584.             Print("Masochism is not implemented.\n");
  1585.         else
  1586.             /* attacking another player */
  1587.             PlayerHitPlayer(target);
  1588.         fi;
  1589.         else
  1590.         /* attacking a monster */
  1591.         a := target@p_mFightAction;
  1592.         if a ~= nil then
  1593.             call(a, void)(target);
  1594.         else
  1595.             StandardAttack(me, target, here);
  1596.         fi;
  1597.         fi;
  1598.         true
  1599.     fi
  1600.     fi
  1601. corp;
  1602.  
  1603. /*
  1604.  * verbs to allow briefer output during combat.
  1605.  */
  1606.  
  1607. define tp_fight proc v_fightterse()bool:
  1608.     thing me;
  1609.  
  1610.     me := Me();
  1611.     if me@p_pFightTerse and not me@p_pFightSuperTerse then
  1612.     Print("You are already in fightterse mode.\n");
  1613.     false
  1614.     else
  1615.     me -- p_pFightSuperTerse;
  1616.     me@p_pFightTerse := true;
  1617.     Print("fightterse mode set.\n");
  1618.     true
  1619.     fi
  1620. corp;
  1621.  
  1622. define tp_fight proc v_fightsuperterse()bool:
  1623.     thing me;
  1624.  
  1625.     me := Me();
  1626.     if me@p_pFightSuperTerse then
  1627.     Print("You are already in fightsuperterse mode.\n");
  1628.     false
  1629.     else
  1630.     me@p_pFightTerse := true;
  1631.     me@p_pFightSuperTerse := true;
  1632.     Print("fightsuperterse mode set.\n");
  1633.     true
  1634.     fi
  1635. corp;
  1636.  
  1637. define tp_fight proc v_fightverbose()bool:
  1638.     thing me;
  1639.  
  1640.     me := Me();
  1641.     if not me@p_pFightTerse then
  1642.     Print("You are already in fightverbose mode.\n");
  1643.     false
  1644.     else
  1645.     me -- p_pFightTerse;
  1646.     me -- p_pFightSuperTerse;
  1647.     Print("fightverbose mode set.\n");
  1648.     true
  1649.     fi
  1650. corp;
  1651.  
  1652. Verb1(G, "status", 0, v_status).
  1653. Synonym(G, "status", "st").
  1654. Verb1(G, "wield", 0, v_wield).
  1655. Verb1(G, "hit", 0, v_hit).
  1656. Synonym(G, "hit", "fight").
  1657. Synonym(G, "hit", "attack").
  1658. Synonym(G, "hit", "kill").
  1659. Synonym(G, "hit", "h").
  1660. Synonym(G, "hit", "k").
  1661. Verb0(G, "fightterse", 0, v_fightterse).
  1662. Synonym(G, "fightterse", "fightbrief").
  1663. Verb0(G, "fightsuperterse", 0, v_fightsuperterse).
  1664. Synonym(G, "fightsuperterse", "fightsuperbrief").
  1665. Verb0(G, "fightverbose", 0, v_fightverbose).
  1666.  
  1667. unuse tp_fight
  1668.